#include "pari-python.h"
#include "gen.h"
#include "args.h"
#include "errors.h"

PyGEN* create_gen(GEN initVal) { // the initVal is cloned and disposed
	if(initVal==NULL) return NULL;
	CATCH_PARI {
		SET_ERROR;
		return NULL;
	} TRY_PARI {
		GEN val = gclone(initVal);
		PyGEN* self;
		self = PyObject_New(PyGEN, type_ptr);
		self->val = val;
		return self;
	} ENDCATCH_PARI
}

void gen_dealloc(PyObject* _self) {
	PyGEN* self = (PyGEN*) _self;
	CATCH_PARI {
		SET_ERROR;
	} TRY_PARI {
		gunclone(self->val);
	} ENDCATCH_PARI
	PyObject_DEL(self);
}

static PyObject*
gen_str(PyObject* _self) {
	PyGEN* self = (PyGEN*) _self;
	CATCH_PARI {
		SET_ERROR;
		return NULL;
	} TRY_PARI {
		char* res = GENtostr(self->val);
		//printf("string returned %s\n", res);
		PyObject* ret = Py_BuildValue("s", res);
		free(res);
		return ret;
	} ENDCATCH_PARI
}

static int
gen_compare(PyObject* _obj1, PyObject* _obj2) {
	pari_sp __av = avma;
	GEN gen1 = gen_cast(_obj1);
	if(gen1==NULL) {
		avma = __av;
		return -1;
	}			
	GEN gen2 = gen_cast(_obj2);
	if(gen2==NULL) {
		avma = __av;
		return -1;
	}			
	CATCH_PARI {
		SET_ERROR;
		avma = __av;
		return -1;
	} TRY_PARI {
		int res = gcmp(gen1, gen2);
		avma = __av;
		return res;
	} ENDCATCH_PARI
}

static PyObject*
gen_richcompare(PyObject* _obj1, PyObject* _obj2, int op) {
	pari_sp __av = avma;
	GEN gen1 = gen_cast(_obj1);
	if(gen1==NULL) {
		avma = __av;
		return NULL;
	}			
	GEN gen2 = gen_cast(_obj2);
	if(gen2==NULL) {
		avma = __av;
		return NULL;
	}			
	CATCH_PARI {
		SET_ERROR;
		avma = __av;
		return NULL;
	} TRY_PARI {
		GEN res_gen;
		switch(op) {
		    case Py_LT:
                res_gen = glt(gen1, gen2);
                break;
            case Py_LE:
                res_gen = gle(gen1, gen2);
                break;
            case Py_EQ:
                res_gen = geq(gen1, gen2);
                break;
            case Py_NE:
                res_gen = gne(gen1, gen2);
                break;
            case Py_GT:
                res_gen = ggt(gen1, gen2);
                break;
            case Py_GE:
                res_gen = gge(gen1, gen2);
                break;
        }
        PyObject* res = (res_gen==gen_0) ? Py_False : Py_True;
		avma = __av;
		Py_INCREF(res);
		return res;
	} ENDCATCH_PARI
}

static PyObject*
gen_call(PyObject* _obj, PyObject* args, PyObject*) {
	PyGEN* obj = (PyGEN*) _obj;
	GEN val = obj->val;
	bool error = false;
	int x = 0;
	for(int i = 0; i<PyTuple_Size(args); i++) {
		int ind = get_long_arg(args, &x, false, 0, &error);
		if(error) return NULL;
		val = gen_item_gen(val, ind);
	}
	return (PyObject*) create_gen(val);
}

/*
static int
gen_print(PyObject* _self, FILE *fp, int flags) {
	PyGEN* self = (PyGEN*) _self;
	CATCH_PARI {
		SET_ERROR;
		return NULL;
	} TRY_PARI {
		char* res = GENtostr(self->val);
		if(!(flags&Py_PRINT_RAW) && store_output) {
			Py_INCREF(_self);
			int index = PyList_Size(results);
			if(PyList_Append(results, _self)<0) {
				free(res);
				return -1;
			}
			if(fprintf(fp, PRINT_FORMAT, index, res)<0) {
				free(res);
				return -1;
			}
		} else {
			if(fprintf(fp, res)<0) {
				free(res);
				return -1;
			}
		}
		free(res);
		return 0;
	} ENDCATCH_PARI
}*/

PyTypeObject pari_GEN = {
	PyObject_HEAD_INIT(NULL)
	0,
	"pari.GEN", //char *tp_name; /* For printing */
	sizeof(PyGEN), 0, //int tp_basicsize, tp_itemsize; /* For allocation */
	gen_dealloc, //destructor tp_dealloc;
    0, //printfunc tp_print;
    0, //getattrfunc tp_getattr;
    0, //setattrfunc tp_setattr;
    gen_compare, //cmpfunc tp_compare;
    gen_str, //reprfunc tp_repr;

    /* Method suites for standard classes */

	&gen_as_number, //PyNumberMethods *tp_as_number;
	&gen_as_sequence, //PySequenceMethods *tp_as_sequence;
	0, //PyMappingMethods *tp_as_mapping;

    /* More standard operations (here for binary compatibility) */

    0, //hashfunc tp_hash;
    gen_call, //ternaryfunc tp_call;
    gen_str, //reprfunc tp_str;
    0, //getattrofunc tp_getattro;
    0, //setattrofunc tp_setattro;

    /* Functions to access object as input/output buffer */
    0, //PyBufferProcs *tp_as_buffer;

    /* Flags to define presence of optional/expanded features */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, //long tp_flags;

    0, //char *tp_doc; /* Documentation string */
	/* Assigned meaning in release 2.0 */
	/* call function for all accessible objects */
	0,//traverseproc tp_traverse;

	/* delete references to contained objects */
	0,//inquiry tp_clear;

	/* Assigned meaning in release 2.1 */
	/* rich comparisons */
	gen_richcompare,//richcmpfunc tp_richcompare;

};
